/*
	Copyright (c) 2009-2011, Stefan Webb.

	This file is part of SimpleML. Please read the file "license.txt", 
	which contains the conditions under which this software is distributed.
*/

/*!
	\file parser.h
	\author Stefan Webb
	\brief Definition of Parser class.
*/

#ifndef PARSER_H
#define PARSER_H

//#include "stdafx.h"

/*! \brief Enumeration of node types */
enum NodeType {
	/*! Start tag, such as <document>. End tags are implicitly represented. */
	TAG,

	/*! Character data. */
	CHARDATA,
	
	/*! Processing instruction, e.g., <?lua ... ?>. */
	PROCESSING,

	/*! Processing instruction values, i.e. character data inside <?... ?> */
	INSTRUCTIONS,	// Remove in future
	
	/*! Comment in following format <!-- -->. */
	COMMENT,
	
	/*! Attribute of tag. */
	ATTR_IDENT,	// Change this to ATTRIBUTE in future

	/*! Value of tag attribute. */
	ATTR_VALUE,	// Remove in future

	/*! Pseudo node, which is child of tag (if it has children). */
	// PSEUDONODE,

	/*! End of file pseudo-node type. */
	ENDOFFILE,

	/*! Parser error state. */
	PARSER_ERROR,

	/*! Post processing state. */
	PARSER_SUCCESS
};

/*! \brief Enumeration of navigation directions */
enum Direction {
	HERE, NEXT, PREVIOUS, CHILD, PARENT, SIBLING, START, TOP, ATTRIBUTE, VALUE
};

/*! \brief Enumeration of insert positions. */
enum Position {
	BEFORE, AFTER, INSIDE
};

/*! \brief Structure for element node */
struct Node {
	/*! \brief Offset of node content in string */
	utf16* offset;

	/*! \brief Length of node content in string */
	unsigned int length;

	/*! \brief Index to child node, if any */
	unsigned int child;

	/*! \brief Index to sibling node, if any */
	unsigned int sibling;
};

// This is in anticipation of next version of program.
struct PseudoNode {
	unsigned int firstAttribute;
	unsigned int firstChild;
	unsigned int firstSibling; // This may be redundant, change to "reserved".
	// unsigned int parent; // Maybe remove firstSibling in favour of this.
	unsigned int lastSibling;
};

/*! \brief Bit set for descriptor to indicate that this node has a parent
	linking to it directly. */
#define HASPARENT 1 << 3

/*! \brief Bit set for descriptor to indicate that this node has a sibling. */
#define HASSIBLING 1 << 4

/*! \brief Bit set for descriptor to indicate that this node has a child. */
#define HASCHILD 1 << 5

/*! \brief Bit set for descriptor to indicate that this tag node uses a namespace. */
#define HASNAMESPACE 1 << 6

/*! \brief Bit set for descriptor to indicate that this tag has one or more attributes. */
#define HASATTRIBUTE 1 << 7

/*! \brief Bit set for descriptor to indicate that this value node uses single quotes. */
#define HASSINGLEQUOTES 1 << 7

/*! \brief Bits used for node type in descriptor. */
#define NODETYPEBITS 7

// *** Remove Agent in the future, can use PARSER_ERROR in state.
// *** Maybe make Agent into a State template class, and then use State<NodeType>.
// *** For other classes that use Agent do State<bool>.

/*! \brief Class for parsing text in XML-like syntax and producing a "document 
	object model" that can be manipulated. */
class Parser : public Agent, protected Unicode
{
public:
	/*! \brief Parse contents of specified text file.
		\param filename Filename of file to parse. */
	Parser(wchar_t* filename);

	/*! \brief Parse string as if it was the contents of a text file. 
		\param text String to parse.
		\param length Length of string. */
	Parser(utf16* text, unsigned int length);

	/*! \brief Default destructor. */
	~Parser();

	/*! \brief Save as specifed filename.
		\param filename Filename for output file.
		\return True if operation succeeds, false otherwise. */
	bool save(wchar_t* filename);

	/*! \brief Print file to output window.
		\return True if operation succeeds, false otherwise. */
	bool print();

	/*! \brief Parse a text string with specified length.
		\param text String to parse.
		\param length Length of string.
		\return True if operation succeeds, false otherwise. */
	bool open(utf16* text, unsigned int length);

	/*! \brief Parse a file from specified filename.
		\param filename File to parse.
		\return True if operation succeeds, false otherwise. */
	bool open(wchar_t* filename);

	/*! \brief Close parsed file and free memory. */
	void close();

	/*! \brief Move position of cursor in specified direction.
		\param Direction wherein to move the cursor.
		\return True if operation succeeds. False, otherwise. */
	bool navigate(Direction direction);

	/*! \brief Node type of current node.
		\return Node type of node pointed to by cursor. */
	NodeType type();

	/*! \brief Gets pointer to the contents of the current node.
		\return String pointed to by cursor. */
	utf16* value();

	/*! \brief Length of contents of current node, used in conjunction with value().
		\return Length of current node. */
	unsigned int length();

	/*! \brief Gets depth of the current node.
		\return Node depth of the node pointed to by cursor. */
	unsigned int depth();

	/*! \brief Determine if current node has any children.
		\return True if current node has children, false otherwise. */
	bool hasChild();

	/*! \brief Determine if current node has any siblings.
		\return True if current node has siblings, false otherwise. */
	bool hasSibling();

	/*! \brief Determine if current node has any attributes (if tag).
		\return True if current node is a tag and has at least one attribute,
		false otherwise. */
	bool hasAttribute();

	/*! \brief Determine if current node has a namespace.
		\return True if current node has a namespace. False, otherwise. */
	bool hasNamespace();

	/*! \brief Determine if current node has a parent.
		\return True if current node has parent. False, otherwise. */
	bool hasParent();

	/*! \brief Determine if current node has a sibling beneath it.
		\return True if current node has sibling beneath. False, otherwise. */
	bool hasNextSibling();

	/*! \brief Determine if an attribute value is contained in single or double quotes.
		\return True if attribute value uses single quotes. False, otherwise. */
	bool hasSingleQuotes();

	/*! \brief Determine if current node has an attribute value.
		\return True if current node is an attribute identifier and has a value.
		False, otherwise. */
	bool hasValue();

	/*! \brief Extract current node into string.
		\return Pointer to string of current node value. Must be deleted by caller. */
	wchar_t* string();

	/*! \brief Convert value to floating point number.
		\return Double containing number in current node. */
	double number();

	/*! \brief Compares specified string to current string.
		\param string String to which to compare with current value.
		\return True if string and current value are equal. False, otherwise. */
	bool isEqual(wchar_t* string);

	/*! \brief Gets pointer to the namespace of the current node (if it has one).
		\return Namespace string. Note that this is not zero terminated). */
	utf16* valueNamespace();

	/*! \brief Extract current node namespace into string.
		\return Pointer to string of current node's namespace. Must be deleted 
		by caller. */
	wchar_t* stringNamespace();

	/*! \brief Get length of namespace.
		\return Length of namespace of current node. */
	unsigned int lengthNamespace();

	/*! \brief Insert node into document.
		\param position Position to insert node.
		\param nodeType Type of node to insert.
		\param offset Offset of node data. This will be deleted [] at destruction.
		\param length Length of node data.
		\return True if operation succeeds. False, otherwise. */
	bool insert(Position position, NodeType nodeType, utf16* offset, unsigned int length);

	/*! \brief Remove current node, freeing its and its children's memory. */
	void remove();

	/*! \brief Modify current node, being able to replace its value. Do not set more than one node to the same
		pointer outside string_. (Otherwise, it will be deleted [] twice and will cause an error.) */
	bool modify(utf16* offset, unsigned int length);

protected:
	/*! \brief Default constructor. */
	Parser();

	/*! \brief Text object of file. */
	Text text_;

	/*! \brief String of text. */
	utf16* string_;

	/*! \brief Filename. */
	wchar_t* filename_;

	/*! \brief Length of string. */
	unsigned int length_;

	/*! \brief Maximum depth of document. */
	unsigned int maximumDepth_;

	/*! \brief Node that starts document. */
	unsigned int startNode_;

	/*! \brief Get current node of cursor.
		\return Node pointed to by cursor. */
	unsigned int currentNode();

	/*! \brief Node bucket. */
	Bucket<Node> nodes;

	/*! \brief Descriptor bucket. */
	Bucket<unsigned char> descriptors;

	/*! \brief Cursor stack. */
	Stack<unsigned int> cursor;

	/*! \brief Siblings stack. */
	Stack<unsigned int> siblings;

	/*! \brief Priority queue containing free nodes. */
	PriorityQueue<unsigned int> freeNodes;

	/*! \brief Output file in XML-like syntax. */
	wchar_t* output();

	/*! \brief Determine if specified attribute identifier node has value.
		\param node Node to check.
		\return True if specified attribute identifier node has value. False, otherwise. */
	bool hasValue(unsigned int node);

	/*! \brief Get type of specified node.
		\param node Node for which to retrieve type.
		\return Type of specified node. */
	NodeType type(unsigned int node);

	/*! \brief Parse text. 
		\return True if operation succeeds. False, otherwise. */
	bool parse();
		
	/*! \brief Determine if specified node has parent.
		\param node Node to check.
		\return True if specified node has parent. False, otherwise. */
	bool hasParent(unsigned int node);

	/*! \brief Determine if specified node has sibling.
		\param node Node to check.
		\return True if specified node has sibling. False, otherwise. */
	bool hasSibling(unsigned int node);

	/*! \brief Determine if specified node has child.
		\param node Node to check.
		\return True if specified node has child. False, otherwise. */
	bool hasChild(unsigned int node);

	/*! \brief Determine if specified node has namespace.
		\param node Node to check.
		\return True if specified node has namespace. False, otherwise. */
	bool hasNamespace(unsigned int node);

	/*! \brief Determine if specified node has attribute.
		\param node Node to check.
		\return True if specified node has attribute. False, otherwise. */
	bool hasAttribute(unsigned int node);

	/*! \brief Determine if specified node has a sibling beneath it.
		\param node Node to check.
		\return True if specified node has sibling beneath. False, otherwise. */
	bool hasNextSibling(unsigned int node);

	/*! \brief Determine if specified attribute value node uses single quotes.
		\param node Node to check.
		\return True if specified attribute value node uses single quotes. False,
		otherwise. */
	bool hasSingleQuotes(unsigned int node);

	/*! \brief Determine if specified node was created at time of file parsing.
		\param node Node to check.
		\return True if specified node was created at time of file parsing. False, otherwise. */
	bool isOriginalNode(unsigned int node);

	/*! \brief Determine if the current node is the first child.
		\return True if current node is the first child. False, otherwise. */
	bool isFirstChild();

	/*! \brief Get the final sibling.
		\param node Node for which to get final sibling.
		\return The final sibling on this branch. */
	unsigned int finalSibling(unsigned int node);

	/*! \brief Get the previous sibling.
		\param node Node for which to get previous sibling.
		\return The previous sibling of this node. */
	unsigned int previousSibling(unsigned int node);

	/*! \brief Get first non-attribute child of node.
		\param node Node for which to get the first child.
		\return First child of specified node. */
	unsigned int firstChild(unsigned int node);

	/*! \brief Free memory for all nodes. */
	void deleteNodes();

	/*! \brief Free memory for node and add to free nodes.
		\param node Node to delete. */
	void deleteNode(unsigned int node);

	/*! \brief Initialize variables. */
	void initialize();

	/*! \brief Parse tag. */
	void parseTag();

	/*! \brief Parse character data. */
	void parseCharacterData();

	/*! \brief Parse processing instructions. */
	void parseInstructions();

	/*! \brief Parse comment. */
	void parseComment();

	/*! \brief Parse attribute.
		\return True if operation is successful. False otherwise. */
	bool parseAttribute();

	/*! \brief Give parser error.
		\param string Error string for error message. */
	void parseError(wchar_t* string);

	/*! \brief Extract identifier. Identifiers are terminated by whitespace.
		\return True if operation was successful, false otherwise. */
	bool parseIdentifier();

	/*! \brief Skip white space, while checking errors and end-of-file.
		\return True if operation was successful, false otherwise. */
	bool parseWhitespace();

	/*! \brief Increment current string position. */
	void incrementPosition();

	/*! \brief Decrement current string position. */
	void decrementPosition();

	/*! \brief Continue parsing until character is reached. Sets isEmpty flag.
		\param character Character to parse until.
		\return True if operation was successful, false otherwise. */
	bool parseUntil(utf32 character);

	/*! \brief Continue parsing until string is reached. Sets isEmpty flag.
		\param string String to search for.
		\return True if string was found or end-of-file. False, otherwise. */
	bool parseUntil(utf16* string);

	/*! \brief Search for string at current position.
		\param string String to search for.
		\return True if operation was successful, false otherwise. */
	bool parseString(utf16* string);

	/*! \brief Search for string at current position with given length.
		\param string String to search for.
		\return True if operation was successful, false otherwise. */
	bool parseString(utf16* string, unsigned int length);

	/*! \brief Parse attribute value in string.
		\return True if operation was successful, false otherwise. */
	bool parseValue();

	/*! \brief Save node and descriptor.
		\param type Type of node to save. */
	void saveNode(NodeType type);

	/*! \brief Get next free node.
		\return Index of next free node. */
	unsigned int nextFreeNode();

	/*! \brief Create a node with given contents and type.
		\param nodeType Type of node to insert.
		\param offset Offset of desired node contents.
		\param length Length of offset string.
		\return Zero if error. Otherwise, index of created node. */
	unsigned int createNode(NodeType nodeType, utf16* offset, unsigned int length);

	/*! \brief Trim white space from end of string.
		\param start Start of string.
		\param end End of string.
		\return Pointer to new end of string. */
	utf16* trimWhitespace(utf16* start, utf16* end);

	/*! \brief State of parser. */
	NodeType state_;

	/*! \brief Various positions of parser. */
	utf16* thisNode_, *thisLine_, *position_, *final_, *endNode_;

	/*! \brief Currnet line and column, and the first child node. */
	unsigned int countLines_, countColumns_, firstChildNode_;

	/*! \brief Tags for parser. */
	bool isFirstChild_, hasNamespace_, isEmpty_, hasSmallQuotes_;
};

#endif